using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Collections;

namespace LogFileViewer
{
    public partial class ProgressWindow : Form
    {
        public ProgressWindow(DataAccess dataAccess)
        {
            InitializeComponent();
            this.dataAccess = dataAccess;
        }

        /// <summary>
        ///     Returns true if everything went fine and
        ///     false if Cancel button was pressed or an error occured.
        /// </summary>
        /// <returns></returns>
        public bool RunFileIndexer()
        {
            // Set window title for this session
            this.Text = "Create log file indices";
            this.threadFileIndexer.RunWorkerAsync();
            DialogResult result = this.ShowDialog();
            if (result == DialogResult.Cancel)
            {
                return false;
            }
            return true;
        }

        private DataAccess dataAccess;

        public void StopProcess()
        {
            if (this.threadFileIndexer.IsBusy)
            {
                this.threadFileIndexer.CancelAsync();
            }
            this.Close();
        }

        private void ChangeProgressDescription(string description)
        {
            this.description = description;
            this.ChangeProgressDescription();
        }

        string description;

        private void ChangeProgressDescription()
        {
            if (this.lblDescription.InvokeRequired)
            {
                this.lblDescription.Invoke(new MethodInvoker(ChangeProgressDescription));
            }
            else
            {
                this.lblDescription.Text = this.description;
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.StopProcess();
        }

        private void threadFileIndexer_DoWork(object sender, DoWorkEventArgs e)
        {
            this.dataAccess.FnProvider.ResetFileIterator();
            LogFile logfile = this.dataAccess.FnProvider.GetNextFile();
            while (logfile != null)
            {
                FileStream fileStream = File.OpenRead(logfile.Filename);
                {
                    if (fileStream == null || fileStream.Length == 0)
                    {
                        this.threadFileIndexer.CancelAsync();
                        return;
                    }

                    string description = "Creating indexes for file \"" + logfile.Filename + "\" ...";
                    Console.Write(description);
                    DateTime startTime = DateTime.Now;
                    long pieceSize = fileStream.Length / 100;

                    this.ChangeProgressDescription(description);

                    ArrayList[] lists = new ArrayList[5];

                    string tempLine;
                    long lineStart = 0;
                    fileStream.Position = lineStart;
                    long piecesDone = 0;
                    do
                    {
                        if (this.threadFileIndexer.CancellationPending)
                        {
                            e.Cancel = true;
                            Console.WriteLine("DEBUG: Indexer thread cancelled.");
                            return;
                        }

                        StringBuilder cache = new StringBuilder();
                        int found = -1;
                        int size = 256;
                        while (found == -1)
                        {
                            byte[] bytes = new byte[size];
                            fileStream.Read(bytes, 0, size);
                            // Look for the next linebreak after search position
                            string s = Encoding.Default.GetString(bytes);
                            cache.Append(s);
                            found = s.IndexOf("\r\n");
                            if ((cache.Length + size) > fileStream.Length)
                            {
                                break;
                            }
                        }

                        if (found == -1)
                        {
                            tempLine = null;
                        }
                        else
                        {
                            found += cache.ToString().Length - size;
                            tempLine = cache.ToString().Substring(0, found);
                        }

                        if (tempLine == null)
                        {
                            continue;
                        }

                        // Find out what message type
                        if (tempLine.IndexOf(".") == 2 && tempLine.IndexOf(" ") == 10)
                        {
                            int start = tempLine.IndexOf('[');
                            int end = tempLine.IndexOf(']');
                            if (start == -1 || end == -1)
                            {
                                continue;
                            }
                            string type = tempLine.Substring(start + 1, end - start - 1);
                            if (type == null)
                            {
                                continue;
                            }
                            for (int i = 0; i < MessageType.Length(); i++)
                            {
                                if (type.Equals(MessageType.StringArray()[i]))
                                {
                                    if (lists[i] == null)
                                    {
                                        lists[i] = new ArrayList();
                                    }

                                    // Add current log entry line number to list
                                    lists[i].Add(lineStart);

                                    // Additionally count existing log entries in file
                                    MessageTypes currentType = MessageType.MessageTypeAt(i);
                                    if (dataAccess.EveryLogEntriesAmount == null)
                                    {
                                        dataAccess.EveryLogEntriesAmount = new Dictionary<MessageTypes, int>();
                                    }
                                    if (!dataAccess.EveryLogEntriesAmount.ContainsKey(currentType))
                                    {
                                        dataAccess.EveryLogEntriesAmount.Add(currentType, 0);
                                    }
                                    dataAccess.EveryLogEntriesAmount[currentType]++;

                                    break;
                                }
                            }

                        }

                        // End of file is reached
                        if (found == -1 && fileStream.Length < (fileStream.Position + size))
                        {
                            break;
                        }

                        // Set next line start delimiter
                        lineStart += found + 2;
                        fileStream.Position = lineStart;

                        if (fileStream.Position > piecesDone)
                        {
                            //this.progressWindow.SetProgressBarValue(1);


                            // Update Progress Bar
                            int percent = (int)((fileStream.Position * 100) / fileStream.Length);
                            this.threadFileIndexer.ReportProgress(percent, "Indexer");

                            //Console.WriteLine(">> " + fileStream.Position);
                            piecesDone += pieceSize;
                        }

                    } while (tempLine != null);

                    // Save arrays in variable
                    Dictionary<MessageTypes, long[]> indexes = new Dictionary<MessageTypes, long[]>(MessageType.Length());
                    for (int i = 0; i < MessageType.Length(); i++)
                    {
                        ArrayList list = lists[i];
                        if (list != null)
                        {
                            long[] tempArray = (long[])list.ToArray(typeof(long));
                            MessageTypes type = MessageType.MessageTypeAt(i);
                            if (!indexes.ContainsKey(type))
                            {
                                indexes.Add(MessageType.MessageTypeAt(i), tempArray);
                            }
                            else
                            {
                                indexes[MessageType.MessageTypeAt(i)] = tempArray;
                            }
                        }
                        int percent = (100 * (i + 1)) / MessageType.Length();
                    }

                    Console.WriteLine("content: " + indexes.Count);
                    this.dataAccess.LineIndexes = indexes;

                    TimeSpan timespan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                    Console.WriteLine("Finished after: " + timespan.ToString() + " HH:mm:ss");
                }
                logfile = this.dataAccess.FnProvider.GetNextFile();
            }

        }

        private void threadFileIndexer_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Update progressbar
            if (e.ProgressPercentage <= 100)
            {
                this.progressBar.Value = e.ProgressPercentage;
                this.lblPercent.Text = e.ProgressPercentage + " %";
            }

            // May be useful later
            //string progressType = (string)e.UserState;
        }

        private void threadFileIndexer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.StopProcess();
        }
    }
}